#pragma once

#include <memory>
#include <vulkan/vulkan.hpp>
#include <defines.hpp>
#include <device.hpp>

#define APP_NAME "NaturalEngineApplication"
#define ENGINE_NAME "NaturalEngine"

namespace NaturalEngine {
    /// 设备扩展信息提供者
    class IDeviceExtensionsProvider {
    public:
        virtual ~IDeviceExtensionsProvider() = default;

        virtual void GetExtensions(vector<cstr> &) const = 0;
    };

    /// 设备表面提供者
    class IDeviceSurfaceProvider {
    public:
        virtual ~IDeviceSurfaceProvider() = default;

        virtual void CreateSurface(const vk::Instance &, vk::SurfaceKHR &) const = 0;
    };

    struct QueueContext final {
        std::optional<u32> FamilyIdx;
        //        std::optional<u32> QueueIdx;
        vk::Queue Queue;
    };

    struct VkSwapChainInfo {
        vk::Extent2D ImageExtent;
        u32 ImageCount;
        vk::SurfaceFormatKHR Format;
        vk::SurfaceTransformFlagsKHR Transform;
        vk::PresentModeKHR Present;
    };

    class VkDevice : public Device {
    public:
        explicit VkDevice(const IDeviceExtensionsProvider &, const IDeviceSurfaceProvider &);

        ~VkDevice();

        void CreateSharedSwapchainKHR(vk::SwapchainKHR &swapchain, vk::SwapchainCreateInfoKHR &createInfo) const;

        VkSwapChainInfo QuerySwapChainInfo(i32 width, i32 height, u32 imageCount) const;

    private:
        void CreateInstance(const vk::ApplicationInfo &, const vector<cstr> &);

        void GetPhysicalDevice();

        void QueryQueueFamilyIdx();

        void CreateLogicDevice();

        void QueryQueue();

        vk::Instance _instance;
        vk::Device _logicDevice;
        vk::PhysicalDevice _physicalDevice;
        vk::SurfaceKHR _surface;
        QueueContext _graphicsQueue;
        QueueContext _presentQueue;
    };
}
